#pragma once
#include <QOpenGLWindow>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QMatrix4x4>
#include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject>
#include <QSequentialAnimationGroup>
#include <QPropertyAnimation>
#include <QOpenGLExtraFunctions>
#include <QTimer>
#include <qopengl.h>
#include <QList>
#include <QVector3D>


class Logo
{
public:
	Logo() {
		m_data.resize(2500 * 6);

		const GLfloat x1 = +0.06f;
		const GLfloat y1 = -0.14f;
		const GLfloat x2 = +0.14f;
		const GLfloat y2 = -0.06f;
		const GLfloat x3 = +0.08f;
		const GLfloat y3 = +0.00f;
		const GLfloat x4 = +0.30f;
		const GLfloat y4 = +0.22f;

		quad(x1, y1, x2, y2, y2, x2, y1, x1);
		quad(x3, y3, x4, y4, y4, x4, y3, x3);

		extrude(x1, y1, x2, y2);
		extrude(x2, y2, y2, x2);
		extrude(y2, x2, y1, x1);
		extrude(y1, x1, x1, y1);
		extrude(x3, y3, x4, y4);
		extrude(x4, y4, y4, x4);
		extrude(y4, x4, y3, x3);

		const int NumSectors = 100;

		for (int i = 0; i < NumSectors; ++i) {
			GLfloat angle = (i * 2 * M_PI) / NumSectors;
			GLfloat angleSin = qSin(angle);
			GLfloat angleCos = qCos(angle);
			const GLfloat x5 = 0.30f * angleSin;
			const GLfloat y5 = 0.30f * angleCos;
			const GLfloat x6 = 0.20f * angleSin;
			const GLfloat y6 = 0.20f * angleCos;

			angle = ((i + 1) * 2 * M_PI) / NumSectors;
			angleSin = qSin(angle);
			angleCos = qCos(angle);
			const GLfloat x7 = 0.20f * angleSin;
			const GLfloat y7 = 0.20f * angleCos;
			const GLfloat x8 = 0.30f * angleSin;
			const GLfloat y8 = 0.30f * angleCos;

			quad(x5, y5, x6, y6, x7, y7, x8, y8);

			extrude(x6, y6, x7, y7);
			extrude(x8, y8, x5, y5);
		}
	}
	const GLfloat* constData() const { return m_data.constData(); }
	int count() const { return m_count; }
	int vertexCount() const { return m_count / 6; }

private:
	void quad(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2, GLfloat x3, GLfloat y3, GLfloat x4, GLfloat y4) {
		QVector3D n = QVector3D::normal(QVector3D(x4 - x1, y4 - y1, 0.0f), QVector3D(x2 - x1, y2 - y1, 0.0f));

		add(QVector3D(x1, y1, -0.05f), n);
		add(QVector3D(x4, y4, -0.05f), n);
		add(QVector3D(x2, y2, -0.05f), n);

		add(QVector3D(x3, y3, -0.05f), n);
		add(QVector3D(x2, y2, -0.05f), n);
		add(QVector3D(x4, y4, -0.05f), n);

		n = QVector3D::normal(QVector3D(x1 - x4, y1 - y4, 0.0f), QVector3D(x2 - x4, y2 - y4, 0.0f));

		add(QVector3D(x4, y4, 0.05f), n);
		add(QVector3D(x1, y1, 0.05f), n);
		add(QVector3D(x2, y2, 0.05f), n);

		add(QVector3D(x2, y2, 0.05f), n);
		add(QVector3D(x3, y3, 0.05f), n);
		add(QVector3D(x4, y4, 0.05f), n);
	}
	void extrude(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) {
		QVector3D n = QVector3D::normal(QVector3D(0.0f, 0.0f, -0.1f), QVector3D(x2 - x1, y2 - y1, 0.0f));

		add(QVector3D(x1, y1, +0.05f), n);
		add(QVector3D(x1, y1, -0.05f), n);
		add(QVector3D(x2, y2, +0.05f), n);

		add(QVector3D(x2, y2, -0.05f), n);
		add(QVector3D(x2, y2, +0.05f), n);
		add(QVector3D(x1, y1, -0.05f), n);
	}
	void add(const QVector3D& v, const QVector3D& n) {
		GLfloat* p = m_data.data() + m_count;
		*p++ = v.x();
		*p++ = v.y();
		*p++ = v.z();
		*p++ = n.x();
		*p++ = n.y();
		*p++ = n.z();
		m_count += 6;
	}

	QList<GLfloat> m_data;
	int m_count = 0;
};



class CanvasWindow : public QOpenGLWindow
{
	Q_OBJECT;
	Q_PROPERTY(float z READ z WRITE setZ);
	Q_PROPERTY(float r READ r WRITE setR);
	Q_PROPERTY(float r2 READ r2 WRITE setR2);
public:
	CanvasWindow();
	~CanvasWindow();

	void initializeGL();
	void resizeGL(int w, int h);
	void paintGL();

	float z() const { return m_eye.z(); }
	void setZ(float v);

	float r() const { return m_r; }
	void setR(float v);
	float r2() const { return m_r2; }
	void setR2(float v);
private slots:
	void startSecondStage();
private:
	QOpenGLTexture* m_texture = nullptr;
	QOpenGLShaderProgram* m_program = nullptr;
	QOpenGLBuffer* m_vbo = nullptr;
	QOpenGLVertexArrayObject* m_vao = nullptr;
	Logo m_logo;
	int m_projMatrixLoc = 0;
	int m_camMatrixLoc = 0;
	int m_worldMatrixLoc = 0;
	int m_myMatrixLoc = 0;
	int m_lightPosLoc = 0;
	QMatrix4x4 m_proj;
	QMatrix4x4 m_world;
	QVector3D m_eye;
	QVector3D m_target = { 0, 0, -1 };
	bool m_uniformsDirty = true;
	float m_r = 0;
	float m_r2 = 0;
};